package com.tongda.core.message.tts;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import javaFlacEncoder.EncodingConfiguration;
import javaFlacEncoder.FLACEncoder;
import javaFlacEncoder.FLACFileOutputStream;
import javaFlacEncoder.StreamConfiguration;

/**
 * thanks very much to google
 */
public class WavToJFlac {
	/** Maximum number of bytes to read from file at once */
	private static final int MAX_READ = 16384;

	/** Status enum for encode result */
	public enum Status {

		UNKNOWN,

		FULL_ENCODE,

		GENERAL_ERROR,

		INTERNAL_ERROR,

		UNSUPPORTED_FILE,

		FILE_IO_ERROR,

		UNSUPPORTED_SAMPLE_SIZE,

		OUTPUT_FILE_ERROR,

		OK
	}

	FLACEncoder flac = null;
	StreamConfiguration sc = null;
	EncodingConfiguration ec = null;
	File outFile = null;
	int lastTotalSamples = 0;
	boolean useThreads;

	/**
	 * 构造flacencoder对象
	 */
	public WavToJFlac() {
		flac = new FLACEncoder();
		sc = new StreamConfiguration();
		ec = new EncodingConfiguration();
		useThreads = true;
	}

	/**
	 * 判断是否使用多线程
	 * 
	 * @param val
	 */
	public void useThreads(boolean val) {
		useThreads = val;
	}

	private void adjustConfigurations() {// (AudioFormat format) {
		int sampleRate = 16000;// (int)format.getSampleRate();
		int sampleSize = 16; // (int)format.getSampleSizeInBits();
		int channels = 1;// (int)format.getChannels();

		sc.setSampleRate(sampleRate);
		sc.setBitsPerSample(sampleSize);
		sc.setChannelCount(channels);
	}

	/**
	 * 给encoder设置流配置信息
	 * 
	 * @param config
	 */
	public void setStreamConfig(StreamConfiguration config) {
		sc = config;
	}

	/**
	 * 设置编码配置信息
	 * 
	 * @param config
	 */
	public void setEncodingConfig(EncodingConfiguration config) {
		ec = config;
	}

	/**
	 * 打开流方法
	 * 
	 * @return
	 */
	private Status openStream() {
		Status status = Status.OK;
		boolean result = flac.setStreamConfiguration(sc);
		result = result & flac.setEncodingConfiguration(ec);
		if (!result)
			status = Status.INTERNAL_ERROR;
		else {
			FLACFileOutputStream fout = null;
			fout = new FLACFileOutputStream(outFile.getPath());
			if (status == Status.OK) {
				flac.setOutputStream(fout);
				try {
					flac.openFLACStream();
				} catch (IOException e) {
					status = Status.INTERNAL_ERROR;
				}
			} else
				status = Status.OUTPUT_FILE_ERROR;
		}
		return status;
	}

	/**
	 * 将输入文件wav转为输出flac文件
	 * 
	 * @param inputFile
	 * @param outputFile
	 * @return
	 */
	public Status encode(File inputFile, File outputFile) {
		Status status = Status.FULL_ENCODE;
		this.outFile = outputFile;
		FileInputStream sin = null;
		try {
			sin = new FileInputStream(inputFile);
		} catch (FileNotFoundException e1) {
			status = Status.FILE_IO_ERROR;
			e1.printStackTrace();
		} finally {
			if (status != Status.FULL_ENCODE)
				return status;
		}

		try {
			adjustConfigurations();
			openStream();
			int frameSize = 2;// format.getFrameSize();
			int sampleSize = 16;// format.getSampleSizeInBits();
			int bytesPerSample = sampleSize / 8;
			if (sampleSize % 8 != 0) {
				// end processing now
				Exception newEx = new Exception(
						Status.UNSUPPORTED_SAMPLE_SIZE.name());
				throw newEx;

			}
			int channels = 1;// format.getChannels();
			boolean bigEndian = false;// format.isBigEndian();
			byte[] samplesIn = new byte[(int) MAX_READ];
			int samplesRead;
			int framesRead;
			int[] sampleData = new int[MAX_READ * channels / frameSize];
			int blockSize = sc.getMaxBlockSize();
			int unencodedSamples = 0;
			int totalSamples = 0;
			while ((samplesRead = sin.read(samplesIn, 0, MAX_READ)) != -1) {
				// System.err.println("Read: " + read);
				framesRead = samplesRead / (frameSize);
				if (bigEndian) {
					for (int i = 0; i < framesRead * channels; i++) {
						int lower8Mask = 255;
						int temp = 0;
						int totalTemp = 0;
						for (int x = bytesPerSample - 1; x >= 0; x++) {
							int upShift = 8 * x;
							if (x == 0)// don't mask...we want sign
								temp = ((samplesIn[bytesPerSample * i + x]) << upShift);
							else
								temp = ((samplesIn[bytesPerSample * i + x] & lower8Mask) << upShift);
							totalTemp = totalTemp | temp;
						}
						sampleData[i] = totalTemp;
					}
				} else {
					for (int i = 0; i < framesRead * channels; i++) {
						int lower8Mask = 255;
						int temp = 0;
						int totalTemp = 0;
						for (int x = 0; x < bytesPerSample; x++) {
							int upShift = 8 * x;
							if (x == bytesPerSample - 1)// don't mask...we want
														// sign
								temp = ((samplesIn[bytesPerSample * i + x]) << upShift);
							else
								temp = ((samplesIn[bytesPerSample * i + x] & lower8Mask) << upShift);
							totalTemp = totalTemp | temp;
						}
						sampleData[i] = totalTemp;
					}
				}
				if (framesRead > 0) {
					flac.addSamples(sampleData, framesRead);
					unencodedSamples += framesRead;
				}
				// if(unencodedSamples > blockSize*100) {
				if (useThreads)// Thread.yield();//
					unencodedSamples -= flac.t_encodeSamples(unencodedSamples,
							false);
				else
					unencodedSamples -= flac.encodeSamples(unencodedSamples,
							false);
				totalSamples += unencodedSamples;
				// unencodedSamples = 0;

				// }
				// System.err.println("read : "+ samplesRead);
			}
			totalSamples += unencodedSamples;
			if (useThreads)
				unencodedSamples -= flac
						.t_encodeSamples(unencodedSamples, true);
			else
				unencodedSamples -= flac.encodeSamples(unencodedSamples, true);
			// unencodedSamples = 0;
			lastTotalSamples = totalSamples;
		} catch (IOException e) {
			status = Status.FILE_IO_ERROR;
		} catch (Exception e) {
			status = Status.GENERAL_ERROR;
			String message = e.getMessage();
			if (message == null) {
				e.printStackTrace();
			} else if (message.equals(Status.UNSUPPORTED_SAMPLE_SIZE.name()))
				status = Status.UNSUPPORTED_SAMPLE_SIZE;
		}
		return status;
	}

	/**
	 * 获取采样的总数
	 * 
	 * @return
	 */
	public int getLastTotalSamplesEncoded() {
		return this.lastTotalSamples;
	}
}